#pragma once

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<functional>



namespace myThread{
    // 直接用包装器包装一个调用函数 这个函数就是未来创建线程执行的任务！
    using func_t = std::function<void()>;

    static uint32_t thread_id = 1;

    class Thread{
    private:

        // 通过Linux下特有的函数，将线程名字设置到 “线程局部存储”
        void SetThreadName(){
            int setname_id = pthread_setname_np(_tid, _name.c_str());
            if(setname_id == 0) {std::cout << "线程名字" << _name << "被设置到系统中" << std::endl;}
            else {std::cerr << "线程名字" << _name << "被设置到系统失败" << std::endl;}
        }

        void SetDetach(){
            std::cout << "线程" << _name << "分离" << std::endl;
            _is_detach = true;
        }

        void SetRunning(){_is_running = true;}
        void DisRunning() {_is_running = false;}


        // 不加static是有问题的，不能传给pthread_create的函数指针 -> 因为类内函数默认有this指针(两个参数)
        static void* Routine(void* args){
            Thread* self = static_cast<Thread*>(args);
            // 调用函数

            // 这里就先不管如何通过函数把返回值带出来，等到实现一份带有模板参数的就可以了
            self->_func();
            // 这个函数到这里结束了，马上设置运行状态
            self->DisRunning();
            return nullptr;
        }
        // 但是，加了static，没有this指针，没办法用类内的变量和函数！ 所以，调用这个函数的时候传。
        // 所以，这就是为什么pthread_create最后一个参数传的是this指针


    public:    
        Thread(func_t func)
            :_tid(0),
            _func(func),
            _is_detach(false),
            _is_running(false),
            _result(nullptr)
        {
            char* name = new char[64]{0};
            snprintf(name, 64, "thread_%d", thread_id++);
            _name = name;
            delete[] name;
        }

        // 析构函数什么也不用写
        ~Thread(){}

        // 启动线程
        bool Start(){
            // 如果线程已经启动，那就直接返回 -> 不能让线程重复启动
            if(_is_running){
                std::cout << "线程" << _name << "已经被创建!" << std::endl;
                return false;
            }

            // 这个Routine函数是从外部传来的void()，让类内部的void*(void*)函数进行回调
            else{
                int createThread_id = pthread_create(&_tid, nullptr, Routine, this);
                if(createThread_id == 0) {
                    std::cout << "线程" << _name << "创建成功" << std::endl;
                    SetThreadName(); // 把名字设置到系统内
                    SetRunning(); // 将该进程设置为运行状态 

                    // 为了防止出现一种情况：即在线程Start之前就Detach
                    // 如果这里不这么做，会导致系统层面并没有真正的Detach该线程
                    if(_is_detach) SetDetach();
                    return true;
                    return true;
                }
                else{
                    std::cerr << "线程" << _name << "创建失败" << std::endl;
                    return false;
                }
            }
        }

        // 分离线程
        void Detach(){
            // 已经设置了分离了，那就不操作了
            if(_is_detach) return;

            if(_is_running){
                // 如果当前线程已经跑起来了，那么就需要手动的调用接口来设置分离
                pthread_detach(_tid);
            }
            SetDetach();
        }


        // 终止线程
        void Stop(){
            // 只有线程处于运行状态才要终止
            if(_is_running){
                int cancelThread_id = pthread_cancel(_tid);
                if(cancelThread_id == 0) {
                    std::cout << "线程" << _name << "终止" << std::endl;
                    DisRunning();
                }
                else {std::cout << "线程" << _name << "终止失败" << std::endl;}
            }
        }

        // 回收线程
        void Join(){
            if(_is_detach){
                std::cerr << "线程" << _name << ", tid[" << _tid << "]" << "被分离， 无法回收" << std::endl;
            }
            else{
                // 线程的回收不需要判断线程是否还在运行
                int joinThread_id = pthread_join(_tid, nullptr); // 先不关心返回值的问题
                if(joinThread_id == 0){
                    std::cerr << "线程" << _name << "回收成功" << std::endl;
                }
            }
        }

        

    private:
        pthread_t _tid;    // 线程的id
        func_t _func;      // 线程未来执行的任务
        bool _is_detach;   // 判断线程是否需要分离
        bool _is_running;  // 判断线程是否处于运行状态
        void* _result;     // 线程结束后的返回值
        std::string _name; // 线程名字
    };





}